﻿using System;
using Oxage.CNC.Models;

namespace Oxage.Simulator
{
	[InlineBinding(typeof(IMoveInline))]
	public class MoveStepMaker : IStepMaker
	{
		#region Public methods
		/// <summary>
		/// Move tool to the specified relative position; linear interpolation
		/// </summary>
		public void MoveRelative(IStepDriver driver, NPoint point)
		{
			#region Initialization
			NPoint dir = new NPoint();
			NPoint result = new NPoint();
			NPoint previous = new NPoint();
			NPoint<double> k = new NPoint<double>(); //Coeficient (y = kx + n)
			var dimensions = point.GetKeys();
			double max = 0;

			//Find the dimension with maximum steps to perform
			foreach (var dim in dimensions)
			{
				double x = point[dim];
				max = Math.Max(max, Math.Abs(x));
			}

			//Calculate coeficients and directions
			foreach (var dim in dimensions)
			{
				double x = point[dim];
				k[dim] = (double)x / max;
				dir[dim] = Math.Sign(x);
			}
			#endregion

			int i;
			for (i = 1; i <= max; i++)
			{
				foreach (var dim in dimensions)
				{
					int x = (int)(k[dim] * i);
					result[dim] = (x != previous[dim] ? dir[dim] : 0); //Apply movement only if x changed
					previous[dim] = x;
				}

				driver.Step(result);
			}
		}

		/// <summary>
		/// Move tool to the specified absolute position; linear interpolation
		/// </summary>
		public void MoveAbsolute(IStepDriver driver, NPoint point)
		{
			var relative = new NPoint();

			//Convert absolute to relative coordinates
			foreach (var dim in point.GetKeys())
			{
				relative[dim] = point[dim] - driver.Cursor[dim];
			}

			//Perform movement
			MoveRelative(driver, relative);
		}
		#endregion

		#region IStepMaker Members
		public void Run(IStepDriver driver, object data)
		{
			if (driver == null)
			{
				throw new ArgumentNullException("driver");
			}

			var inline = data as IMoveInline;
			if (inline == null)
			{
				throw new ArgumentException("Expected IMoveInline");
			}

			MoveAbsolute(driver, new NPoint((int)inline.EndPoint.X, (int)inline.EndPoint.Y));
		}
		#endregion
	}
}
